use std::vec;

use crate::db::db_knowledge;
use crate::db::entities::knowledge::Model;
use crate::rest_api::api_knowledge::{CreateParams, Knowledge, MultiUpdateParams, SearchParams, UpdateParams};
use crate::rest_api::service::{srv_category, srv_subject};

use anyhow::Result;

use rbase::define::{ApiError, BaseError};
use rbase::id_utils::{opt_str2id, str2id};
use rbase_iam::db::define::SYSTEM_ID;
use rbase_iam::db::{db_tag, db_tag_relation};
use rbase_iam::rest_api::api_tag::Tag;
use rbase_iam::rest_api::api_user::UserToken;
use rbase_iam::rest_api::service::srv_tag;

pub async fn knowledge_from_model(knowledge_model: &Model) -> Result<Knowledge> {
    let knowledge = Knowledge::from_model(knowledge_model);
    Ok(knowledge)
}

pub async fn fing_knowledge_by_id_with_user(user_token: &UserToken, knowledge_id_str: &String) -> Result<Model, ApiError> {
    let knowledge_id = str2id(knowledge_id_str)?;
    let knowledge_model = db_knowledge::find_by_id_and_owner_ids(knowledge_id, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(knowledge_id_str.to_string()))?;
    return Ok(knowledge_model);
}

pub async fn find_knowledge_by_id(user_token: &UserToken, knowledge_id_str: &String) -> Result<Knowledge, ApiError> {
    let knowledge_model = fing_knowledge_by_id_with_user(user_token, knowledge_id_str).await?;
    Ok(knowledge_from_model(&knowledge_model).await?)
}

pub async fn delete_knowledge_by_id(user_token: &UserToken, knowledge_id: &String) -> Result<(), ApiError> {
    let knowledge_model = fing_knowledge_by_id_with_user(user_token, knowledge_id).await?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && knowledge_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    db_knowledge::delete_by_id(knowledge_model.id).await?;
    db_tag_relation::delete_by_other_id(knowledge_model.id).await?;
    Ok(())
}

pub async fn update_knowledge_by_id(user_token: &UserToken, knowledge_id: &String, new_knowledge: &UpdateParams) -> Result<(), ApiError> {
    let subject_id = srv_subject::validate_subject_id(user_token, &new_knowledge.subject_id).await?;
    let category_id = srv_category::validate_category_id(user_token, &new_knowledge.category_id).await?;
    let old_model = fing_knowledge_by_id_with_user(user_token, knowledge_id).await?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && old_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    let new_model = Model {
        id: old_model.id,
        name: new_knowledge.name.clone(),
        content: new_knowledge.content.clone(),
        subject_id,
        category_id,
        ..Default::default()
    };
    db_knowledge::update(&new_model).await?;

    // 更新tags
    let ids = match srv_tag::validate_tag_ids(user_token, &new_knowledge.tag_ids).await? {
        Some(ids) => ids,
        None => {
            return Ok(());
        }
    };
    srv_tag::update_other_id_with_tag_ids(user_token, old_model.id, &ids).await?;

    Ok(())
}

pub async fn create_knowledge(user_token: &UserToken, new_knowledge: &CreateParams) -> Result<Knowledge> {
    let subject_id = srv_subject::validate_subject_id(user_token, &new_knowledge.subject_id).await?;
    let category_id = srv_category::validate_category_id(user_token, &new_knowledge.category_id).await?;
    let new_model = Model {
        name: new_knowledge.name.clone(),
        content: new_knowledge.content.clone(),
        owner_id: Some(user_token.id),
        subject_id,
        category_id,
        ..Default::default()
    };
    let created_model = db_knowledge::insert(new_model).await?;
    Ok(knowledge_from_model(&created_model).await?)
}

pub async fn search_common(user_token: &UserToken, search_params: &SearchParams) -> Result<(Vec<Model>, i64)> {
    let subject_id = srv_subject::validate_subject_id(user_token, &search_params.subject_id).await?;
    let category_id = srv_category::validate_category_id(user_token, &search_params.category_id).await?;

    let tag_ids = srv_tag::validate_tag_ids(user_token, &search_params.tag_ids).await?;
    let owner_ids = if user_token.is_admin { None } else { Some(vec![SYSTEM_ID, user_token.id]) };
    let (knowledges, total) = db_knowledge::search(&db_knowledge::SearchParams {
        name: search_params.name.clone(),
        category_id,
        subject_id,
        offset: search_params.offset,
        limit: search_params.limit,
        order_by: search_params.order_by.clone(),
        order: search_params.order.clone(),
        cols: search_params.cols.clone(),
        tag_ids,
        owner_ids,
    })
    .await?;

    Ok((knowledges, total))
}

pub async fn search(user_token: &UserToken, search_params: &SearchParams) -> Result<(Vec<Knowledge>, i64)> {
    let (knowledges, total) = search_common(user_token, search_params).await?;

    let mut knowledge_results = Vec::new();
    for knowledge in knowledges {
        knowledge_results.push(knowledge_from_model(&knowledge).await?);
    }

    Ok((knowledge_results, total))
}

pub async fn multi_update_knowledge(user_token: &UserToken, update_params: &MultiUpdateParams) -> Result<(), ApiError> {
    if let Some(condition) = &update_params.condition
        && let Some(update) = &update_params.update
    {
        let mut search_params = condition.clone();
        search_params.offset = None;
        search_params.order = None;
        search_params.order_by = None;
        search_params.limit = None;
        search_params.cols = Some(vec!["id".to_string()]);

        let (knowledges, _total) = search_common(user_token, &search_params).await?;

        // 更新知识
        let subject_id = opt_str2id(&update.subject_id)?;
        let category_id = opt_str2id(&update.category_id)?;
        let mut new_model = Model {
            name: update.name.clone(),
            content: update.content.clone(),
            subject_id,
            category_id,
            ..Default::default()
        };
        for knowledge in &knowledges {
            new_model.id = knowledge.id;
            db_knowledge::update(&new_model).await?;
        }

        // 更新tags
        if let Some(tag_ids) = srv_tag::validate_tag_ids(user_token, &update.tag_ids).await? {
            for knowledge in &knowledges {
                srv_tag::update_other_id_with_tag_ids(user_token, knowledge.id, &tag_ids).await?;
            }
        }
    } else if let Some(items) = &update_params.items {
        for item in items {
            let knowledge_id = str2id(&item.id)?;
            let old_model = db_knowledge::find_by_id(knowledge_id)
                .await?
                .ok_or_else(|| BaseError::ResourceNotFound(item.id.to_string()))?;
            // 普通用户不能修改系统资源
            if !user_token.is_admin && old_model.owner_id == Some(SYSTEM_ID) {
                return Err(BaseError::PermissionDenied.into());
            }

            // 更新知识
            let subject_id = srv_subject::validate_subject_id(user_token, &item.subject_id).await?;
            let category_id = srv_category::validate_category_id(user_token, &item.category_id).await?;
            let new_model = Model {
                id: knowledge_id,
                name: item.name.clone(),
                content: item.content.clone(),
                subject_id,
                category_id,
                ..Default::default()
            };
            db_knowledge::update(&new_model).await?;

            // 更新tags
            if let Some(tag_ids) = srv_tag::validate_tag_ids(user_token, &item.tag_ids).await? {
                srv_tag::update_other_id_with_tag_ids(user_token, knowledge_id, &tag_ids).await?;
            }
        }
    }
    Ok(())
}

pub async fn list_tags_by_id(user_token: &UserToken, knowledge_id_str: &String) -> Result<Vec<Tag>, ApiError> {
    let knowledge_id = str2id(knowledge_id_str)?;

    let _ = db_knowledge::find_by_id_and_owner_ids(knowledge_id, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(knowledge_id_str.to_string()))?;
    let tags = db_tag::list_by_other_id_and_owner_ids(knowledge_id, &user_token.owner_ids).await?;
    Ok(tags.iter().map(|tag| Tag::from_model(tag)).collect())
}
